package com.hehao.java_basic.concurrent;

import lombok.Data;

/**
 * synchronized 关键字使用demo
 * @author Damon
 * @date 2023/6/7
 */
@Data
public class SynchronizedDemo {

    /**
     * 同步代码块形式1：只有一把锁的情况，锁为当前对象this，也可指定自定义锁
     */
    @Data
    public static class CodeBlock1 implements Runnable {

        @Override
        public void run() {
            synchronized (this) {
                System.out.println("我是线程" + Thread.currentThread().getName());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "结束");
            }

        }
        public static void main(String[] args) {
            CodeBlock1 codeBlock1 = new CodeBlock1();
            Thread thread1 = new Thread(codeBlock1);
            Thread thread2 = new Thread(codeBlock1);
            thread1.start();
            thread2.start();
        }

    }

    /**
     * 同步代码块形式2：有多把锁，其中一把锁为当前对象this，也可指定自定义锁
     */
    @Data
    public static class CodeBlock2 implements Runnable{
        private final Object block1;
        private final Object block2;
        public CodeBlock2(){
            block1 = new Object();
            block2 = new Object();
        }

        @Override
        public void run() {
            // 这个代码块使用的是第一把锁，当他释放后，后面的代码块由于使用的是第二把锁，因此可以马上执行
            synchronized (block1) {
                System.out.println("block1锁,我是线程" + Thread.currentThread().getName());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("block1锁,"+Thread.currentThread().getName() + "结束");
            }

            synchronized (block2) {
                System.out.println("block2锁,我是线程" + Thread.currentThread().getName());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("block2锁,"+Thread.currentThread().getName() + "结束");
            }
        }

        public static void main(String[] args) {
            CodeBlock2 codeBlock2 = new CodeBlock2();
            Thread t1 = new Thread(codeBlock2);
            Thread t2 = new Thread(codeBlock2);
            t1.start();
            t2.start();
        }
    }

    /**
     * 同步代码块形式3：只有一把锁的情况，锁为类锁
     */
    @Data
    public static class CodeBlock3 implements Runnable{

        @Override
        public void run() {
            // 所有线程需要的锁都是同一把
            synchronized (CodeBlock3.class) {
                System.out.println("我是线程" + Thread.currentThread().getName());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "结束");
            }
        }

        public static void main(String[] args) {
            CodeBlock3 object1 = new CodeBlock3();
            CodeBlock3 object2 = new CodeBlock3();
            Thread t1 = new Thread(object1);
            Thread t2 = new Thread(object2);
            t1.start();
            t2.start();
        }
    }


    /**
     * 方法锁1，锁对象为this，锁普通方法
     */
    @Data
    public static class NormalFunctionSync implements Runnable{

        @Override
        public void run() {
            normalMethod();
        }

        public synchronized void normalMethod() {
            System.out.println(this.hashCode());
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "结束");
        }

        public static void main(String[] args) {
            NormalFunctionSync run1 = new NormalFunctionSync();
            NormalFunctionSync run2 = new NormalFunctionSync();
            Thread thread1 = new Thread(run1);
            Thread thread2 = new Thread(run2);
            thread1.start();
            thread2.start();
        }

    }


    /**
     * 方法锁2，锁为类锁，锁静态方法
     */
    @Data
    public static class StaticFunctionSync implements Runnable{

        @Override
        public void run() {
            staticMethod();
        }

        // synchronized用在静态方法上，默认的锁就是当前所在的Class类，所以无论是哪个线程访问它，需要的锁都只有一把
        public static synchronized void staticMethod() {
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "结束");
        }

        public static void main(String[] args) {
            StaticFunctionSync run1 = new StaticFunctionSync();
            StaticFunctionSync run2 = new StaticFunctionSync();
            Thread thread1 = new Thread(run1);
            Thread thread2 = new Thread(run2);
            thread1.start();
            thread2.start();
        }

    }

    /**
     *可重入锁(递归锁)，线程进入子程序会自动获取锁(前提是子程序是同一个类或对象锁)
     * 重入==>监控计数器会+、- 1，直到计数器为0释放锁
     */
    @Data
    public static class ReentrantLockSync{
        public static void main(String[] args) {
            ReentrantLockSync demo = new ReentrantLockSync();
            demo.method1();
        }

        private synchronized void method1() {
            System.out.println(Thread.currentThread().getId() + ": method1()"+ ":"+this);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            method2();
        }

        private synchronized void method2() {
            System.out.println(Thread.currentThread().getId()+ ": method2()"+ ":"+this);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            method3();
        }

        private synchronized void method3() {
            System.out.println(Thread.currentThread().getId()+ ": method3()"+ ":"+this);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
